import * as _ from "lodash"
import httpClient from "../httpclient"

class District {
  public id: string
  public code: string
  public name: string
  public position: number
}

export class Country extends District {
  public provinces: Province[]
}

export class Region extends District {
  public provinces: Province[]
}

export class Province extends District {
  public cities: City[]
}

export class City extends District {
  public counties: County[]
}

export class County extends District {

}

function assignCounty(country: County) {
  return _.assign(new County(), country)
}

function assignCity(city: City) {
  return _.assign(new City(), city, {
    counties: _.map(city.counties, assignCounty),
  })
}

function assignProvince(province: Province) {
  return _.assign(new Province(), province, {
    cities: _.map(province.cities, assignCity),
  })
}

class CountryQuery {
  private _scope: number

  public scope(scope: number): CountryQuery {
    this._scope = scope
    return this
  }

  public list(): Promise<Province[]> {
    return httpClient.get(`/v1/district/countries`, { params: { scope: this._scope } })
      .then(({ data }) => _.map(data, province => _.assign(province, assignProvince)))
  }
}

class RegionQuery {
  private _countryId: string
  private _scope: number

  public countryId(countryId: string): RegionQuery {
    this._countryId = countryId
    return this
  }

  public scope(scope: number): RegionQuery {
    this._scope = scope
    return this
  }

  public list(): Promise<Region[]> {
    return httpClient.get(`/v1/district/countries/${this._countryId}/regions`, {
      params: { scope: this._scope },
    })
      .then(({ data }) => _.map(data, region => _.assign(new Region(), region, {
        provinces: _.map(region.provinces, assignProvince),
      })))
  }
}

class ProvinceQuery {
  private _countryId: string
  private _scope: number

  public countryId(countryId: string): ProvinceQuery {
    this._countryId = countryId
    return this
  }

  public scope(scope: number): ProvinceQuery {
    this._scope = scope
    return this
  }

  public list(): Promise<Province[]> {
    return httpClient.get(`/v1/district/countries/${this._countryId}/provinces`, { params: { scope: this._scope } })
      .then(({ data }) => _.map(data, province => _.assign(province, assignProvince)))
  }
}

class DistrictQuery<T> {
  public _countryId: string
  public _provinceId: string
  public _cityId: string
  public _code: string
  public _scope: number

  private readonly client: (query: DistrictQuery<T>) => Promise<T[]>

  constructor(client: (query: DistrictQuery<T>) => Promise<T[]>) {
    this.client = client
  }

  public countryId(countryId: string): DistrictQuery<T> {
    this._countryId = countryId
    return this
  }

  public provinceId(provinceId: string): DistrictQuery<T> {
    this._provinceId = provinceId
    return this
  }

  public cityId(cityId: string): DistrictQuery<T> {
    this._cityId = cityId
    return this
  }

  public scope(scope: number): DistrictQuery<T> {
    this._scope = scope
    return this
  }

  public code(code: string): DistrictQuery<T> {
    this._code = code
    return this
  }

  public list(): Promise<T[]> {
    return this.client(this)
  }
}

export class DistrictService {

  public static getCountries(): CountryQuery {
    return new CountryQuery()
  }

  public static getRegions(): RegionQuery {
    return new RegionQuery()
  }

  public static getProvinces(): ProvinceQuery {
    return new ProvinceQuery()
  }

  public static getCities(): DistrictQuery<City> {
    return new DistrictQuery<City>((query: DistrictQuery<City>) => {
      return httpClient.get(`/v1/district/provinces/${query._provinceId}/cities`, {
        params: {
          scope: query._scope,
          code: query._code,
        },
      })
        .then(({ data }) => _.map(data, city => _.assign(city, assignCity)))
    })
  }

  public static getCounty(countyId: string): Promise<County> {
    return httpClient.get(`/v1/district/counties/${countyId}`)
      .then(({ data }) => data)
      .then(assignCounty)
  }
}
